package checker

import (
	"mi/compiler/ast"
	"mi/compiler/binder"
	"mi/compiler/token"
)

func (c *checker) initType(t Type, flags TypeFlags) {
	c.typeCount++
	t.setID(c.typeCount)
	t.setFlags(flags)
}

func (c *checker) newIntrinsicType(flags TypeFlags, name string, realName string) *intrinsicType {
	var t = new(intrinsicType)
	t.name = name
	t.realName = realName
	c.initType(t, flags)
	return t
}

func (c *checker) newIntrinsicTypeWithToken(flags TypeFlags, token token.Token) *intrinsicType {
	var t = new(intrinsicType)
	t.name = token.ToString()
	t.realName = token.ToOriginal().ToString()
	c.initType(t, flags)
	return t
}

func (c *checker) newObjectType(flags TypeFlags, symbol *binder.Symbol) *objectType {
	var t = new(objectType)
	t.symbol = symbol
	c.initType(t, flags)
	return t
}

func (c *checker) newInterfaceType(flags TypeFlags, symbol *binder.Symbol) *interfaceType {
	var t = new(interfaceType)
	t.symbol = symbol
	c.initType(t, flags)
	return t
}

func (c *checker) newGenericType(flags TypeFlags, symbol *binder.Symbol) *genericType {
	var t = new(genericType)
	t.symbol = symbol
	c.initType(t, flags)
	return t
}

func (c *checker) newTypeParam(flags TypeFlags, symbol *binder.Symbol) *typeParameter {
	var t = new(typeParameter)
	t.symbol = symbol
	c.initType(t, flags)
	return t
}

func (c *checker) newTypeReferencer(flags TypeFlags, symbol *binder.Symbol) *typeReference {
	var t = new(typeReference)
	t.symbol = symbol
	c.initType(t, flags)
	return t
}

func (c *checker) newStringLiteralType(text string) *stringLiteralType {
	var t = new(stringLiteralType)
	t.text = text
	c.initType(t, TFString)
	return t
}

func (c *checker) newCharLiteralType(ch rune) *numberLiteralType {
	var t = new(numberLiteralType)
	t.value = NewNumberWithRune(ch)

	c.initType(t, TFNumber)
	return t
}

func (c *checker) newNumberLiteralType(text string, isFloat bool) *numberLiteralType {
	var t = new(numberLiteralType)
	t.value = NewNumberWithString(text, isFloat)

	c.initType(t, TFNumber)
	return t
}

func (c *checker) newNumberLiteralTypeWithNumber(flags TypeFlags, number *Number) *numberLiteralType {
	var t = new(numberLiteralType)
	t.value = NewNumberWithNumber(number)

	c.initType(t, flags)
	return t
}

func (c *checker) newNotNullType(flags TypeFlags, tpe Type) *notNullType {
	var t = new(notNullType)
	t.target = tpe
	c.initType(t, flags)
	return t
}

func (c *checker) newInBoundsType(flags TypeFlags, tpe Type) *inBoundsType {
	var t = new(inBoundsType)
	t.target = tpe
	c.initType(t, flags)
	return t
}

func (c *checker) newOutBoundsType(flags TypeFlags, tpe Type) *outBoundsType {
	var t = new(outBoundsType)
	t.target = tpe
	c.initType(t, flags)
	return t
}

func (c *checker) newNoBoundsType(flags TypeFlags) *noBoundsType {
	var t = new(noBoundsType)
	c.initType(t, flags)
	return t
}

func (c *checker) newAliasSymbol(target *binder.Symbol, mapSymbol *binder.Symbol) *binder.Symbol {
	var symbol = new(binder.Symbol)
	symbol.Flags |= target.Flags|binder.Transient
	symbol.Name = target.Name
	symbol.Declarations = make([]ast.Declaration, len(target.Declarations))
	copy(symbol.Declarations, target.Declarations)
	symbol.ValueDeclaration = target.ValueDeclaration

	var link = c.getSymbolLinks(symbol)
	link.target = target
	link.mappedSymbol = mapSymbol
	return symbol
}

// Debug support
var count = 1000000
func newSymbol(flags binder.SymbolFlags, name string) *binder.Symbol {
	var symbol = new(binder.Symbol)
	symbol.Flags = flags | binder.Transient
	symbol.Name = name
	symbol.Count = count
	count ++
	return symbol
}

func newMissingMethodSymbol(parent *binder.Symbol, name string) *binder.Symbol {
	var result = newSymbol(binder.Missing|binder.Method, name)
	result.Parent = parent
	return result
}

func cloneSymbol(symbol *binder.Symbol) *binder.Symbol {
	var result = newSymbol(symbol.Flags, symbol.Name)
	if symbol.Declarations != nil {
		result.Declarations = make([]ast.Declaration, len(symbol.Declarations))
		copy(result.Declarations, symbol.Declarations)
	}
	result.Parent = symbol.Parent
	if symbol.ValueDeclaration != nil {
		result.ValueDeclaration = symbol.ValueDeclaration
	}
	if symbol.Members != nil {
		result.Members = symbol.Members.Clone()
	}
	if symbol.Exports != nil {
		result.Exports = symbol.Exports.Clone()
	}
	return result
}